package algoritmos.numericos.tipos;

import java.util.ArrayList;
import java.util.Date;

import javax.swing.JButton;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import Jama.Matrix;
import algoritmos.Matriz;
import algoritmos.numericos.Supervisado;

/**
 * Clase que desarrolla el algoritmo Paramétrico.
 * @author Alberto Milán
 *
 */
public class Parametrico extends Supervisado implements Runnable{
	
	/**
	 * Lista de matrices de covarianzas por cada clase
	 */
	private ArrayList<Matrix> covarianzas;
	
	/**
	 * Lista de límites de subrangos de entradas
	 */
	private ArrayList<Integer> limites;
	
	/**
	 * Constructor de la clase
	 */
	public Parametrico(JTextArea arg0, JProgressBar arg1, JButton arg2, JButton arg3){
		super();
		covarianzas = new ArrayList<Matrix>();
		limites = new ArrayList<Integer>();
		texto = arg0;
		progreso = arg1;
		botonNuevoDato = arg2;
		botonAprendizaje = arg3;
	}
	
	/**
	 * Genera los centros o medias de cada clase
	 */
	private void generaCentros(){
		centros = new ArrayList<Matrix>();
		Matriz centro = new Matriz(1,dimension);
		int i = 0; 
		for (int j : limites){
			for (int k = i; k < j; k++){
				Matriz temp = (Matriz)entrada.get(k);
				centro.plusEquals(temp);
			}
			centro = centro.divide(j - i);
			centros.add(centro);
			centro = new Matriz(1,dimension);
			i = j;
		}
	}
	
	/**
	 * Genera la lista de matrices de covarianza según los límites marcados
	 * @param limites Lista de enteros que delimita la distribución de entrada
	 */
	private void generaMatricesCovarianza(ArrayList<Integer> limites){
		covarianzas = new ArrayList<Matrix>();
		Matriz covarianza = new Matriz(dimension, dimension);
		int i = 0; // contador de centro de clase
		int j = 0; // contador de inicio de subrango de entrada
		for (Integer k : limites){
			for (int l = j; l < k; l++){
				Matrix dato = (Matrix)entrada.get(l).clone();
				dato.minusEquals(centros.get(i));
				Matrix producto = dato.transpose().times(dato);
				producto.plusEquals(covarianza);
				covarianza = new Matriz(producto);
			}
			if (k -j - 1 > 0)
				covarianza = covarianza.divide(k - j - 1);
			covarianzas.add(covarianza);
			j = k;
			i++;
			covarianza = new Matriz(dimension, dimension);
		}
	}
	

	@Override
	protected boolean convergencia() {
		// No realiza iteraciones
		return true;
	}

	@Override
	protected void aprendizaje() {
		inicializaComponentesAprendizaje();
		generaCentros();
		generaMatricesCovarianza(limites);
		long milis = new Date().getTime() - startTime.getTime();
		String mensaje = new String();
		mensaje += "\nProceso de aprendizaje finalizado\n";
		mensaje += "El proceso ha durado ";
		if (milis > 1000){ // Si ha tardado más de un segundo
			mensaje += (milis / 1000) + " segundos " + milis % 1000 + " ms.\n";
		} else { // Si ha tardado milisegundos
			mensaje += milis + " ms.\n";
		}
		mensaje += "Se ha dado 1 iteración.\n";
		mensaje += "Centros de las clases:\n";
		for (int i = 0; i < numeroDeClases(); i++){
			mensaje += "Clase " + (i+1) + "= " + centros.get(i) + "\n";
		}
		mensaje += "\nMatrices de covarianza de las clases:\n";
		for (int i = 0; i < numeroDeClases(); i++){
			mensaje += "C" + (i+1) + " = " + covarianzas.get(i) + "\n";
		}
		final String text = mensaje;
		SwingUtilities.invokeLater(new Runnable(){
			@Override
			public void run() {
				texto.append(text);
			}
		});
		finalizaComponentesAprendizaje();
	}

	@Override
	public void perteneceAClase(ArrayList<Number> lista) {
		Matriz dato = new Matriz(lista);
		ArrayList<Double> grados = new ArrayList<Double>();
		int i = 0;
		for (Matrix m : centros){
			Matriz centro = (Matriz)m;
			double grado = 0;
			grado += dato.distanciaMahalanobis(centro, (Matriz)covarianzas.get(i));
			grados.add(grado);
			i++;
		}
		texto.append("\nGrados/Porcentajes de pertenencia a cada clase:\n");
		int contador = 0;
		int clase = 0;
		Double minimo = Double.MAX_VALUE;
		for (Double g : grados){
			contador++;
			if (g < minimo){
				minimo = g;
				clase = contador;
			}
			texto.append("Clase " + contador + 
						  " : Distancia de Mahalanobis = " + g + "\n");
		}
		texto.append("El dato " + dato + 
				  " pertenece a la clase " + clase + "\n");
	}
	
	/*=========================================================================*
	 *							Accesores y mutadores                          * 
	 *=========================================================================*/

	public ArrayList<Integer> getLimites() {
		return limites;
	}

	public void setLimites(ArrayList<Integer> limites) {
		this.limites = limites;
	}
	
}
